package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Resource
    private UserInfoFeignClient userInfoFeignClient;

    @Resource
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Override
    public OrderInfoVo trade(TradeVo tradeVo) {
        // 通过ThreadLocal拿用户id
        Long userId = AuthContextHolder.getUserId();

        // 创建OrderInfoVo对象
        OrderInfoVo orderInfoVo = null;

        String itemType = tradeVo.getItemType();
        orderInfoVo = switch (itemType) {
            case "1001" ->  // 处理付款项类型是专辑的结算页
                    dealItemTypeAlbum(tradeVo, userId);
            case "1002" ->  // 处理付款项类型是声音的结算页
                    dealItemTypeTrack(tradeVo, userId);
            case "1003" ->  // 处理付款项类型是vip套餐的结算页
                    dealItemTypeVip(tradeVo, userId);
            default -> orderInfoVo;
        };

        if (orderInfoVo == null) {
            throw new GuiguException(201, "orderInfoVo初始化失败");
        }
        // 为属性赋值
        orderInfoVo.setTimestamp(System.currentTimeMillis());   // 设置时间戳
        String sign = SignHelper.getSign(JSONObject.parseObject(JSONObject.toJSONString(orderInfoVo), Map.class));
        orderInfoVo.setSign(sign);  // 设置签名

        // 将交易号存入redis
        String orderRepeatSubmitKey = userId + ":" + orderInfoVo.getTradeNo();
        redisTemplate.opsForValue().set(orderRepeatSubmitKey, "1");
        return orderInfoVo;
    }

    @Override
    public Map<String, Object> submitOrder(OrderInfoVo orderInfoVo) {
        // 1.业务层的校验
        // 1.1 校验请求的非法性。
        String tradeNo = orderInfoVo.getTradeNo();
        if (!StringUtils.hasText(tradeNo)) {
            throw new GuiguException(201, "非法请求");
        }

        // 1.2 校验订单信息的完整性以及时效性
        String orderInfoVoStr = JSONObject.toJSONString(orderInfoVo);
        Map signMap = JSONObject.parseObject(orderInfoVoStr, Map.class);
        signMap.put("payWay", "");
        SignHelper.checkSign(signMap); // 先在做一次新签名---接着获取之前的签名---最后对比这两个签名是否相等，如果相等代表没有被篡改过 如果不相等 一定被篡改过

        // 1.3 订单重复提交的校验
        //  第一次提交订单之后，未看见后续的动作（1、网络原因 2.接口的并发高，处理慢） 但是你误以为失败或者以为没点上 接着又点击一次。【这就导致了重复提交】
        //  解决办法：对于同一个请求来说，不管调用多少次，数据都要是一致的,产生的结果是符合预期的。
        //  接口幂等性保证：方案：各种锁机制（分布式锁 本地锁） MySQL的唯一索引+防重表+本地事务  Redis+Token(最多)
        //  单端重复提交  ---TODO:多端重复提交如何实现。
        String orderRepeatSubmitKey = AuthContextHolder.getUserId() + ":" + orderInfoVo.getTradeNo();
        String luaScript = """
                if redis.call("exists",KEYS[1])
                then
                    return redis.call("del",KEYS[1])
                else
                    return 0
                end""";
        Long execute = redisTemplate.execute(new DefaultRedisScript<Long>(luaScript, Long.class), List.of(orderRepeatSubmitKey));
        if (execute == null) {
            throw new GuiguException(201, "redis调用异常");
        }
        if (execute == 0) {
            throw new GuiguException(201, "订单重复提交");
        }

        // 2.保存订单信息(核心代码)

        // 3.生成订单编号

        // 4.判断支付方式

        // 5.订单编号返回
        Map<String, Object> map = new HashMap<>();
        map.put("orderNo", "");
        return map;
    }

    /**
     * @description: 处理付款项类型是vip套餐的结算页
     * @author: baosi
     * @date: 2025/3/26 19:26
     */
    private OrderInfoVo dealItemTypeVip(TradeVo tradeVo, Long userId) {
        // 构建结算页对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();

        // 幂等性校验 某一个套餐是否支付过 不需要做 vip可以续期

        // 幂等性校验 某一个套餐是否下单未支付
        Long count = orderInfoMapper.getItemTypeAlbumAndVipIsPadding(userId, tradeVo.getItemType(), tradeVo.getItemId());
        if (count > 0) {
            log.info("当前购买的某一个套餐下单未支付，请勿重复下单!");
            List<Long> exitItemIds = new ArrayList<>();
            exitItemIds.add(tradeVo.getItemId());
            orderInfoVo.setExitItemIdList(exitItemIds);
            return orderInfoVo;
        }

        //设置结算页属性
        String tradeNo = RandomStringUtils.random(10, false, true);
        orderInfoVo.setTradeNo(tradeNo);
        orderInfoVo.setPayWay("");
        orderInfoVo.setItemType(tradeVo.getItemType());

        // 远程调用获取vip配置
        Result<VipServiceConfig> vipServiceConfigResult = userInfoFeignClient.getVipConfigById(tradeVo.getItemId());
        VipServiceConfig serviceConfigResultData = vipServiceConfigResult.getData();
        if (serviceConfigResultData == null) {
            throw new GuiguException(201, "远程查询用户微服务获取套餐信息失败");
        }

        // 构建结算页金额
        BigDecimal originalAmount = serviceConfigResultData.getPrice(); // 获取套餐原始金额
        BigDecimal discountPrice = serviceConfigResultData.getDiscountPrice();// 获取套餐优惠金额
        orderInfoVo.setOriginalAmount(originalAmount.setScale(2, RoundingMode.HALF_UP)); // 原始金额
        orderInfoVo.setDerateAmount(originalAmount.subtract(discountPrice).setScale(2, RoundingMode.HALF_UP)); // 优惠掉多少金额
        orderInfoVo.setOrderAmount(discountPrice.setScale(2, RoundingMode.HALF_UP)); // 实际金额

        // 构建结算页详情
        List<OrderDetailVo> orderDetailVos = new ArrayList<>();
        OrderDetailVo orderDetailVo = new OrderDetailVo();
        orderDetailVo.setItemId(tradeVo.getItemId());
        orderDetailVo.setItemName(serviceConfigResultData.getName());
        orderDetailVo.setItemUrl(serviceConfigResultData.getImageUrl());
        orderDetailVo.setItemPrice(originalAmount.setScale(2, RoundingMode.HALF_UP));// 原始金额
        orderDetailVos.add(orderDetailVo);
        orderInfoVo.setOrderDetailVoList(Lists.newArrayList(orderDetailVos));

        // 构建结算页减免
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
        OrderDerateVo orderDerateVo = new OrderDerateVo();
        orderDerateVo.setDerateType("1406-VIP服务折扣");
        orderDerateVo.setDerateAmount(discountPrice.setScale(2, RoundingMode.HALF_UP));  // 优惠后
        orderDerateVo.setRemarks("套餐参与了优惠");
        orderDerateVoList.add(orderDerateVo);
        orderInfoVo.setOrderDerateVoList(Lists.newArrayList(orderDerateVoList));
        orderInfoVo.setTimestamp(System.currentTimeMillis());

        return orderInfoVo;
    }

    /**
     * @description: 处理付款项类型是声音的结算页
     * @author: baosi
     * @date: 2025/3/26 19:26
     */
    private OrderInfoVo dealItemTypeTrack(TradeVo tradeVo, Long userId) {
        OrderInfoVo orderInfoVo = new OrderInfoVo();

        // 在分集展示声音的时候 已经将买过的声音过滤掉了 不需要再做幂等性校验

        // 查询要买哪些声音
        Long trackId = tradeVo.getItemId();
        Integer trackCount = tradeVo.getTrackCount();

        // 查询声音对象
        Result<TrackInfo> trackInfoResult = albumInfoFeignClient.getTrackInfoByTrackId(trackId);
        TrackInfo trackInfoResultData = trackInfoResult.getData();
        if (trackInfoResultData == null) {
            throw new GuiguException(201, "该声音不存在");
        }

        // 查询专辑对象
        Long albumId = trackInfoResultData.getAlbumId();
        Result<AlbumInfo> albumInfoAndAttrValue = albumInfoFeignClient.getAlbumInfoAndAttrValue(albumId);
        AlbumInfo albumInfo = albumInfoAndAttrValue.getData();
        if (albumInfo == null) {
            throw new GuiguException(201, "该声音对应的专辑不存在");
        }

        // 查询指定集数的声音列表
        Result<List<TrackInfo>> trackListByCurrentTrackId = albumInfoFeignClient.getTrackListByCurrentTrackId(userId, trackId, trackCount);
        List<TrackInfo> trackIdData = trackListByCurrentTrackId.getData();
        if (CollectionUtils.isEmpty(trackIdData)) {
            throw new GuiguException(201, "远程查询专辑微服务获取指定集数的声音失败");
        }

        // 幂等性校验,该声音是否下单但是未支付
        List<Long> submitAndUnPaidTrackIds = orderInfoMapper.getItemTypeTrackIsPadding(userId, tradeVo.getItemType());
        List<TrackInfo> trackInfoList = trackIdData.stream().filter(
                trackInfo -> submitAndUnPaidTrackIds.contains(trackInfo.getId())
        ).toList();

        // 存在已下单的声音
        if (!CollectionUtils.isEmpty(trackInfoList)) {
            List<Long> exitTrackIds = new ArrayList<>();
            for (TrackInfo trackInfo : trackInfoList) {
                exitTrackIds.add(trackInfo.getId());
            }
            orderInfoVo.setExitItemIdList(exitTrackIds);
            log.error("该声音已下单，请勿重复购买！");
            return orderInfoVo;
        } else {
            // 封装结算页数据
            String tradeNo = RandomStringUtils.random(10, false, true); // 订单号
            orderInfoVo.setTradeNo(tradeNo);
            orderInfoVo.setPayWay("");  // 支付方式位置
            orderInfoVo.setItemType(tradeVo.getItemType()); // 付款项目类型
            BigDecimal price = albumInfo.getPrice(); // 单集声音的价格
            BigDecimal originalAmount = trackCount == 0 ? price : price.multiply(new BigDecimal(trackCount));   //购买单集或多集
            orderInfoVo.setOriginalAmount(originalAmount.setScale(2, RoundingMode.HALF_UP));  // 原始金额
            orderInfoVo.setDerateAmount(new BigDecimal("0.00")); // 优惠金额  (声音没有优惠)
            orderInfoVo.setOrderAmount(originalAmount.setScale(2, RoundingMode.HALF_UP)); // 实际金额

            // 封装结算页详情数据
            List<OrderDetailVo> orderDetailVos = new ArrayList<>();
            for (TrackInfo trackInfo : trackIdData) {
                OrderDetailVo orderDetailVo = new OrderDetailVo();
                orderDetailVo.setItemId(trackInfo.getId());
                orderDetailVo.setItemName(trackInfo.getTrackTitle());
                orderDetailVo.setItemUrl(trackInfo.getCoverUrl());
                orderDetailVo.setItemPrice(price.setScale(2, RoundingMode.HALF_UP));    // 每一集声音价格
                orderDetailVos.add(orderDetailVo);
            }
            orderInfoVo.setOrderDetailVoList(Lists.newArrayList(orderDetailVos));

            // 封装结算减免数据
            orderInfoVo.setOrderDerateVoList(Lists.newArrayList());
        }

        // 返回结算页对象
        return orderInfoVo;
    }

    /**
     * @description: 处理付款项类型是专辑的结算页
     * @author: baosi
     * @date: 2025/3/26 19:25
     */
    private OrderInfoVo dealItemTypeAlbum(TradeVo tradeVo, Long userId) {
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        Long itemId = tradeVo.getItemId();

        // 幂等性校验,查询需要购买的专辑是否已经支付过
        Result<Boolean> userPaidAlbum = userInfoFeignClient.getUserPaidAlbum(userId, itemId);
        Boolean aBoolean = userPaidAlbum.getData();
        if (aBoolean == null) throw new GuiguException(201, "远程调用用户微服务获取用户是否购买过专辑失败");
        if (aBoolean) {
            log.info("该专辑已经购买过，请勿重复购买!");
            List<Long> exitItemIds = new ArrayList<>();
            exitItemIds.add(itemId);
            orderInfoVo.setExitItemIdList(exitItemIds);
            return orderInfoVo;
        }

        // 幂等性校验,查询要购买的该专辑是下单但是未支付
        Long count = orderInfoMapper.getItemTypeAlbumAndVipIsPadding(userId, tradeVo.getItemType(), itemId);
        if (count > 0) {
            log.info("当前购买的专辑下单未支付，请勿重复下单!");
            List<Long> exitItemIds = new ArrayList<>();
            exitItemIds.add(itemId);
            orderInfoVo.setExitItemIdList(exitItemIds);
            return orderInfoVo;
        }

        // 为结算页对象赋值
        String tradeNo = RandomStringUtils.random(10, false, true); // 设置十位随机纯数字作为订单交易号
        orderInfoVo.setTradeNo(tradeNo); // 防止订单重复提交--->跟踪订单
        orderInfoVo.setPayWay("");// 支付方式位置
        orderInfoVo.setItemType(tradeVo.getItemType()); // 付款项类型

        // 远程查询专辑对象
        Result<AlbumInfo> albumInfoAndAttrValue = albumInfoFeignClient.getAlbumInfoAndAttrValue(itemId);
        AlbumInfo albumInfoData = albumInfoAndAttrValue.getData();
        if (albumInfoData == null) {
            throw new GuiguException(201, "远程查询专辑信息失败");
        }

        // 远程查询用户信息
        Result<UserInfoVo> userInfo = userInfoFeignClient.getUserInfo(userId);
        UserInfoVo userInfoData = userInfo.getData();
        Assert.notNull(userInfoData, "远程查询用户微服务获取用户信息失败");
        Integer isVip = userInfoData.getIsVip();

        // 获取折扣
        BigDecimal disCount;
        if ((isVip + "").equals("1")) {
            disCount = albumInfoData.getVipDiscount();  //获取vip折扣
        } else {
            disCount = albumInfoData.getDiscount();     //获取普通折扣
        }
        if (disCount.intValue() == -1) {                // 不打折
            disCount = new BigDecimal("10.00");
        }

        // 计算原始金额
        BigDecimal originalPrice = albumInfoData.getPrice();// 专辑的价格 未优惠的
        orderInfoVo.setOriginalAmount(originalPrice.setScale(2, RoundingMode.HALF_UP));  // 专辑的原始金额（未参与打折优惠的）

        // 计算实际金额 实际金额 = 原始金额 * 折扣 / 10
        BigDecimal orderAmount = originalPrice
                .multiply(disCount)
                .divide(new BigDecimal(10), RoundingMode.HALF_UP)
                .setScale(2, RoundingMode.HALF_UP);

        // 计算优惠金额 优惠金额 = 原始金额 - 实际金额
        BigDecimal derateAmount = originalPrice.subtract(orderAmount).setScale(2, RoundingMode.HALF_UP);

        // 为结算页对象赋值
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderAmount(orderAmount);

        // 构建订单详情明细
        List<OrderDetailVo> orderDetailVos = new ArrayList<>();
        OrderDetailVo orderDetailVo = new OrderDetailVo();
        orderDetailVo.setItemId(itemId); // 专辑id
        orderDetailVo.setItemName(albumInfoData.getAlbumTitle()); // 专辑的名字
        orderDetailVo.setItemUrl(albumInfoData.getCoverUrl()); // 专辑的封面
        orderDetailVo.setItemPrice(originalPrice); // 专辑的原始金额
        orderDetailVos.add(orderDetailVo);
        orderInfoVo.setOrderDetailVoList(Lists.newArrayList(orderDetailVos));  // 订单明细：到底买的是什么东西

        // 构建订单减免明细
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
        OrderDerateVo orderDerateVo = new OrderDerateVo();
        orderDerateVo.setDerateType("1405-专辑折扣");
        orderDerateVo.setDerateAmount(derateAmount);
        orderDerateVo.setRemarks("专辑打折了");
        orderDerateVoList.add(orderDerateVo);
        orderInfoVo.setOrderDerateVoList(Lists.newArrayList(orderDerateVoList)); // 减免明细：到底减免的东西是谁
        orderInfoVo.setTimestamp(System.currentTimeMillis()); // 订单结算页数据的时间戳（为了做订单完整性校验）

        // 返回结算页对象
        return orderInfoVo;
    }
}
